package com.wiki.KBS.aspect;

import com.alibaba.fastjson2.JSONObject;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletRequest;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

@Aspect
@Component
public class LogAspect {

    private static final Logger logger = LoggerFactory.getLogger(LogAspect.class);

    /**  定义切入点
     * *：在返回类型的位置，* 表示匹配任意的返回类型。
     * com.wiki.KBS.*.controller：这部分表示类路径的匹配规则。* 是一个通配符，匹配 com.wiki.KBS 包下的任意一级子包，然后要求该子包下必须有 controller 包。
     * ..：在类路径中使用 .. 表示匹配任意数量（包括零个）的子包。这里 ..*Controller 表示 controller 包及其子包下的所有以 Controller 结尾的类。
     * *：在方法名的位置，* 表示匹配任意的方法名。  (..)：表示匹配任意数量和类型的方法参数。
     * 综合起来，这个切入点表达式的含义是：匹配 com.wiki.KBS 包下的任意一级子包中的 controller 包及其子包下的所有以 Controller 结尾的类的任意方法，无论方法的返回类型、方法名和参数是什么。
      */
    @Pointcut("execution(* com.wiki.KBS..*Controller.*(..))")
    public void controllerMethods() {}

    // 在目标方法执行之前执行
    @Before("controllerMethods()")
    public void beforeUserControllerMethod(JoinPoint joinPoint) {
            //  通过连接点拿到参数
        Object[] args = joinPoint.getArgs();
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
        if (requestAttributes != null) {
            HttpServletRequest request = requestAttributes.getRequest();
            // 用于获取当前连接点的签名信息
            Signature signature = joinPoint.getSignature();
            // 通过签名信息获取方法名称
            String methodName = signature.getName();
            logger.info("----------------- AOP 开始------------------------------");
            logger.info("请求地址及方法: {} {}", request.getRequestURL(),request.getMethod());
            logger.info("远程地址: {}",request.getRemoteAddr());
            // signature.getDeclaringTypeName() 返回声明该成员（如方法、字段等）的类的全限定名（包含包名）
            logger.info("类名称及方法: {}, with arguments: {}",signature.getDeclaringTypeName(),methodName);
            logger.info("Before method: {}, with arguments: {}", methodName, args);

            // 打印请求参数
            Object[] args1 = joinPoint.getArgs();
            // LOG.info("请求参数: {}", JSONObject.toJSONString(args));

            Object[] arguments  = new Object[args1.length];
            for (int i = 0; i < args1.length; i++) {
                if (args1[i] instanceof ServletRequest
                        || args1[i] instanceof ServletResponse
                        || args1[i] instanceof MultipartFile) {
                    continue;
                }
                arguments[i] = args1[i];
            }
            // 排除字段，敏感字段或太长的字段不显示
//            String[] excludeProperties = {"password", "file"};
//            // 使用 FastJSON2 的特性排除指定属性
//            String json = JSON.toJSONString(arguments, JSONWriter.Feature.);
            logger.info("请求参数: {}", JSONObject.toJSONString(arguments));



        } else {
            System.out.println("requestAttributes is null, cannot get request.");
        }


    }

    // 在目标方法执行之后执行，无论是否抛出异常
    @After("controllerMethods()")
    public void afterUserServiceMethod(JoinPoint joinPoint) {
        String methodName = joinPoint.getSignature().getName();
        logger.info("----------------- APO After 结束------------------------------");
        logger.info("After method: {}", methodName);
    }

    // 在目标方法正常返回后执行
    @AfterReturning(pointcut = "controllerMethods()", returning = "result")
    public void afterReturningUserServiceMethod(JoinPoint joinPoint, Object result) {
        String methodName = joinPoint.getSignature().getName();
        logger.info("----------------- APO AfterReturning 结束------------------------------");
        logger.info("Method {} returned: {}", methodName, result);
    }

    // 在目标方法抛出异常后执行
    @AfterThrowing(pointcut = "controllerMethods()", throwing = "ex")
    public void afterThrowingUserServiceMethod(JoinPoint joinPoint, Exception ex) {
        String methodName = joinPoint.getSignature().getName();
        logger.info("----------------- APO AfterThrowing 结束------------------------------");
        logger.error("Method {} threw exception: {}", methodName, ex.getMessage());
    }

    // 环绕通知，可以在目标方法执行前后进行增强
    @Around("controllerMethods()")
    public Object aroundUserServiceMethod(org.aspectj.lang.ProceedingJoinPoint pjp) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object result = pjp.proceed();
        long endTime = System.currentTimeMillis();
        String methodName = pjp.getSignature().getName();
        logger.info("Method {} took {} ms to execute", methodName, endTime - startTime);
        logger.info("----------------- AOP 结束------------------------------");
        return result;
    }
}